﻿// // ******************************************************************
// //       /\ /|       @file       CacheBackgroundService.cs
// //       \ V/        @brief      await29
// //       | "")       @author     Await, 285283010@qq.com
// //       /  |
// //      /  \\        @Modified   2025-03-02 14:03
// //    *(__\_\        @Copyright  Copyright (c) , 2025-02-23 10:02 Await
// // ******************************************************************

using System.Collections.Concurrent;
using AutomaticSubtitles.Service.Application.Cahce;
using AutomaticSubtitles.Service.Application.Cahce.Queries;
using AutomaticSubtitles.Service.Application.Language.Queries;
using AutomaticSubtitles.Service.Application.Media.Commands;
using AutomaticSubtitles.Service.DataAccess.Model;

namespace AutomaticSubtitles.Service.BackgroundServices;

/// <summary>
/// 缓存更新服务
/// </summary>
/// <param name="serviceProvider"></param>
/// <param name="redisClient"></param>
public class CacheBackgroundService(IServiceProvider serviceProvider, RedisClient redisClient) : BackgroundService
{
    // 存储变化的文件信息
    private readonly List<FileChangeInfo> _changedPaths = new();

    // 文件系统监听器集合
    private readonly List<FileSystemWatcher> _fileWatchers = new();

    // 记录已监听的目录
    private readonly HashSet<string> _monitoredDirectories = new();

    // 防抖动计时器，避免短时间内多次触发更新
    private System.Timers.Timer _debounceTimer;
    private ILogger<CacheBackgroundService> _logger;
    private IServiceScope _serviceScope;
    private static bool IsRunning { get; set; }
    private static bool IsWantToDoItImmediately { get; set; }
    private static bool IsInitialized { get; set; } = false;

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _serviceScope = serviceProvider.CreateScope();
        _logger = _serviceScope.ServiceProvider.GetRequiredService<ILogger<CacheBackgroundService>>()!;
        _logger.LogWarning("--后台媒体缓存任务 开启");

        // 初始化防抖动计时器
        _debounceTimer = new System.Timers.Timer(2000); // 2秒防抖
        _debounceTimer.Elapsed += async (sender, e) =>
        {
            _debounceTimer.Stop();
            await UpdateRecordData();
        };

        // 初始化Redis订阅
        InitRedisSubscription();

        // 第一次启动时执行全量扫描
        IsRunning = true;
        IsWantToDoItImmediately = true;
        await PerformFullScan();

        // 设置初始化完成标志
        IsInitialized = true;

        // 使用 TaskCompletionSource 控制任务生命周期
        var taskCompletionSource = new TaskCompletionSource<bool>();

        // 监听取消信号
        stoppingToken.Register(() =>
        {
            CleanupFileWatchers();
            taskCompletionSource.TrySetResult(true);
        });

        // 等待任务完成
        await taskCompletionSource.Task;
    }

    /// <summary>
    /// 执行全量扫描
    /// </summary>
    private async Task PerformFullScan()
    {
        try
        {
            _logger.LogWarning("--执行媒体全量扫描");
            await UpdateRecordData();
            await SetupFileWatchers();
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "--媒体全量扫描异常");
        }
        finally
        {
            IsRunning = false;
        }
    }

    /// <summary>
    /// 初始化Redis订阅
    /// </summary>
    private void InitRedisSubscription()
    {
        redisClient.Subscribe(CacheCommon.CacheKey, async (_, x) =>
        {
            if (x is not nameof(CacheBackgroundService)) return;
            _logger.LogWarning("--媒体库刷新 收到缓存更新通知");

            // 如果收到手动刷新通知，执行全量扫描并重新设置监听器
            if (IsInitialized)
            {
                IsWantToDoItImmediately = true;
                await PerformFullScan();
            }
        });
    }

    /// <summary>
    /// 设置文件系统监听器
    /// </summary>
    private async Task SetupFileWatchers()
    {
        // 清理现有的监听器
        CleanupFileWatchers();

        // 获取媒体目录配置
        var eventBus = _serviceScope.ServiceProvider.GetRequiredService<IEventBus>()!;
        var getCachePathList = new GetCachePathList();
        await eventBus.PublishAsync(getCachePathList);

        // 设置电影目录监听
        if (getCachePathList.Result != null)
            foreach (var path in getCachePathList.Result)
            {
                if (string.IsNullOrWhiteSpace(path) || !Directory.Exists(path)) continue;
                CreateFileWatcher(path, true);
            }

        // 设置电视剧目录监听
        if (getCachePathList.TVPlayResult != null)
            foreach (var path in getCachePathList.TVPlayResult)
            {
                if (string.IsNullOrWhiteSpace(path) || !Directory.Exists(path)) continue;
                CreateFileWatcher(path, false);
            }

        _logger.LogInformation($"--已设置 {_fileWatchers.Count} 个目录监听器");
    }

    /// <summary>
    /// 创建单个目录的文件监听器
    /// </summary>
    private void CreateFileWatcher(string path, bool isMovie)
    {
        try
        {
            // 避免重复监听同一目录
            if (_monitoredDirectories.Contains(path)) return;

            var watcher = new FileSystemWatcher(path)
            {
                IncludeSubdirectories = true,
                EnableRaisingEvents = true,
                NotifyFilter = NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.LastWrite
            };

            // 监听文件创建、删除、重命名事件
            watcher.Created += (sender, e) => OnFileSystemChanged(e.FullPath, isMovie, "创建");
            watcher.Deleted += (sender, e) => OnFileSystemChanged(e.FullPath, isMovie, "删除");
            watcher.Renamed += (sender, e) => OnFileSystemChanged(e.FullPath, isMovie, "重命名");

            _fileWatchers.Add(watcher);
            _monitoredDirectories.Add(path);

            _logger.LogInformation($"--已监听目录: {path} ({(isMovie ? "电影" : "电视剧")})");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, $"--设置目录监听失败: {path}");
        }
    }

    /// <summary>
    /// 文件系统变化事件处理
    /// </summary>
    private void OnFileSystemChanged(string path, bool isMovie, string changeType)
    {
        // 只处理视频和字幕文件
        var extension = Path.GetExtension(path).ToLowerInvariant();
        var isVideoFile = extension == ".mkv" || extension == ".mp4" || extension == ".avi" || extension == ".rmvb" || extension == ".iso" || extension == ".m2ts";
        var isSubtitleFile = extension == ".srt" || extension == ".ass" || extension == ".ssa" || extension == ".vtt" || extension == ".sub";

        if (!isVideoFile && !isSubtitleFile && !string.IsNullOrEmpty(extension)) return;

        _logger.LogInformation($"--检测到{(isMovie ? "电影" : "电视剧")}目录变化: {changeType} {path}");

        // 收集变化信息
        lock (_changedPaths)
        {
            _changedPaths.Add(new FileChangeInfo
            {
                Path = path,
                ChangeType = changeType,
                IsMovie = isMovie,
                IsVideoFile = isVideoFile,
                IsSubtitleFile = isSubtitleFile,
                ChangeTime = DateTime.Now
            });
        }

        // 使用防抖动计时器，避免短时间内多次触发更新
        _debounceTimer.Stop();
        _debounceTimer.Start();
    }

    /// <summary>
    /// 清理文件监听器
    /// </summary>
    private void CleanupFileWatchers()
    {
        foreach (var watcher in _fileWatchers)
        {
            watcher.EnableRaisingEvents = false;
            watcher.Dispose();
        }

        _fileWatchers.Clear();
        _monitoredDirectories.Clear();
    }

    /// <summary>
    /// 设置缓存时间
    /// </summary>
    private async Task SetCacheTimeKey()
    {
        var dateTime = DateTime.Now;
        await redisClient.SetAsync<DateTime?>(CacheCommon.CacheTimeKey, dateTime);
    }

    /// <summary>
    /// 更新提交缓存记录
    /// </summary>
    private async Task UpdateRecordData()
    {
        if (IsRunning && !IsWantToDoItImmediately) return;

        IsRunning = true;
        try
        {
            // 记录此次需要处理的路径变化
            List<FileChangeInfo> pathsToProcess;
            lock (_changedPaths)
            {
                pathsToProcess = new List<FileChangeInfo>(_changedPaths);
                _changedPaths.Clear();
            }

            if (IsWantToDoItImmediately || pathsToProcess.Count == 0)
            {
                // 全量扫描模式
                _logger.LogWarning("--执行全量媒体扫描");
                await PerformFullMediaScan();
            }
            else
            {
                // 增量扫描模式
                _logger.LogWarning($"--执行增量媒体扫描，变化文件数: {pathsToProcess.Count}");
                await ProcessChangedFiles(pathsToProcess);
            }

            // 如果是手动触发的刷新，重新设置文件监听器
            if (IsWantToDoItImmediately)
            {
                IsWantToDoItImmediately = false;
                await SetupFileWatchers();
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "--更新媒体缓存异常");
        }
        finally
        {
            IsRunning = false;
        }
    }

    /// <summary>
    /// 执行全量媒体扫描
    /// </summary>
    private async Task PerformFullMediaScan()
    {
        _logger.LogWarning("--更新媒体缓存 开始");
        await SetCacheTimeKey();
        var eventBus = _serviceScope.ServiceProvider.GetRequiredService<IEventBus>()!;
        var unitOfWork = _serviceScope.ServiceProvider.GetRequiredService<IUnitOfWork>()!;
        var getCachePathList = new GetCachePathList();
        await eventBus.PublishAsync(getCachePathList);
        var data = await ProcessingMediaData(getCachePathList);
        var medias = new List<Auto_Media>();
        if (data.MoviesDirMap != null)
            foreach (var keyValuePair in data.MoviesDirMap)
                medias.AddRange(keyValuePair.Value);
        if (data.SeriesDirMap != null)
            foreach (var keyValuePair in data.SeriesDirMap)
                medias.AddRange(keyValuePair.Value);
        var mediaCommandHandler = new AddMediaListDataCommand(medias);
        await eventBus.PublishAsync(mediaCommandHandler);
        //删选 指定的新增的媒体
        _logger.LogWarning("--更新媒体缓存 结束");
        if (mediaCommandHandler.Result != null)
        {
            _logger.LogWarning("--更新字幕缓存 开始");
            var subtitles = await ProcessingSubtitleData(eventBus, mediaCommandHandler.Result);
            // 添加字幕数据
            var addSubListDataCommand = new AddSubListDataCommand(subtitles, mediaCommandHandler.DeleteMedia);
            await eventBus.PublishAsync(addSubListDataCommand);
            _logger.LogWarning("--更新字幕缓存 结束");
        }
    }

    /// <summary>
    /// 处理变化的文件
    /// </summary>
    private async Task ProcessChangedFiles(List<FileChangeInfo> changedFiles)
    {
        if (changedFiles.Count == 0) return;

        try
        {
            var eventBus = _serviceScope.ServiceProvider.GetRequiredService<IEventBus>()!;
            var requiredService = _serviceScope.ServiceProvider.GetService<ILogger<VideoListHelper>>()!;
            var videoListHelper = new VideoListHelper(requiredService);

            // 按目录分组，减少重复扫描
            var directoryGroups = changedFiles
                .GroupBy(f => Path.GetDirectoryName(f.Path))
                .ToList();

            _logger.LogInformation($"--处理 {directoryGroups.Count} 个变化目录");

            var addedOrModifiedMedias = new List<Auto_Media>();
            var removedPaths = new List<string>();

            // 处理每个变化的目录
            foreach (var dirGroup in directoryGroups)
            {
                var directory = dirGroup.Key;
                if (string.IsNullOrEmpty(directory) || !Directory.Exists(directory)) continue;

                var videoChanges = dirGroup.Where(f => f.IsVideoFile).ToList();
                var subtitleChanges = dirGroup.Where(f => f.IsSubtitleFile).ToList();

                // 处理视频文件变化
                if (videoChanges.Any())
                {
                    // 确定这个目录是电影还是电视剧
                    var isMovieDir = videoChanges.First().IsMovie;

                    // 获取当前目录配置
                    var getCachePathList = new GetCachePathList();
                    await eventBus.PublishAsync(getCachePathList);

                    // 检查目录是电影还是电视剧
                    var targetDirs = isMovieDir ? getCachePathList.Result?.ToList() : getCachePathList.TVPlayResult?.ToList();

                    // 创建或修改的文件
                    var createdOrModified = videoChanges
                        .Where(f => f.ChangeType == "创建" || f.ChangeType == "重命名")
                        .Select(f => f.Path)
                        .ToList();

                    // 删除的文件
                    var deleted = videoChanges
                        .Where(f => f.ChangeType == "删除")
                        .Select(f => f.Path)
                        .ToList();

                    // 添加被删除的路径
                    removedPaths.AddRange(deleted);

                    if (createdOrModified.Any())
                    {
                        // 对于新增和修改的文件，进行局部扫描
                        _logger.LogInformation($"--扫描目录: {directory}");

                        // 调用媒体扫描，只处理这个目录
                        NormalScanVideoResult result;

                        if (isMovieDir)
                            // 电影扫描
                            result = await videoListHelper.RefreshMainListAsync(
                                new List<string> { directory },
                                null,
                                getCachePathList.NoCachePath?.ToList());
                        else
                            // 电视剧扫描
                            result = await videoListHelper.RefreshMainListAsync(
                                null,
                                new List<string> { directory },
                                getCachePathList.NoCachePath?.ToList());

                        // 收集扫描到的媒体
                        if (result.MoviesDirMap != null)
                            foreach (var kvp in result.MoviesDirMap)
                                addedOrModifiedMedias.AddRange(kvp.Value);

                        if (result.SeriesDirMap != null)
                            foreach (var kvp in result.SeriesDirMap)
                                addedOrModifiedMedias.AddRange(kvp.Value);
                    }
                }
            }

            // 处理收集到的媒体文件
            if (addedOrModifiedMedias.Any() || removedPaths.Any())
            {
                // 更新媒体库
                var mediaCommandHandler = new AddMediaListDataCommand(addedOrModifiedMedias)
                {
                    RemovedPaths = removedPaths
                };
                await eventBus.PublishAsync(mediaCommandHandler);

                // 如果有新增媒体，处理字幕
                if (mediaCommandHandler.Result != null && mediaCommandHandler.Result.Any())
                {
                    _logger.LogWarning("--更新字幕缓存 开始");
                    var subtitles = await ProcessingSubtitleData(eventBus, mediaCommandHandler.Result);

                    // 添加字幕数据
                    var addSubListDataCommand = new AddSubListDataCommand(subtitles, mediaCommandHandler.DeleteMedia);
                    await eventBus.PublishAsync(addSubListDataCommand);
                    _logger.LogWarning($"--更新字幕缓存 结束，处理了 {subtitles?.Count ?? 0} 个字幕");
                }

                _logger.LogInformation($"--增量更新完成: 新增/修改 {addedOrModifiedMedias.Count} 个媒体，删除 {removedPaths.Count} 个媒体");
            }
            else
            {
                _logger.LogInformation("--没有检测到需要处理的媒体变化");
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "--处理变化文件异常，将在下次执行全量扫描");
            // 出现异常时，标记需要执行全量扫描
            IsWantToDoItImmediately = true;
        }
    }

    /// <summary>
    ///     处理字幕数据
    /// </summary>
    /// <param name="eventBus"></param>
    /// <param name="refreshMainListAsync"></param>
    private async Task<List<Auto_Subtitle>?> ProcessingSubtitleData(IEventBus eventBus, List<Auto_Media> refreshMainListAsync)
    {
        var getCacheKeyDataQuery = new GetLanguageKeyDataQuery();
        await eventBus.PublishAsync(getCacheKeyDataQuery);
        if (getCacheKeyDataQuery.Result == null)
            return null;

        var data = new ConcurrentBag<Auto_Subtitle>();
        var tasks = new List<Task>
        {
            Task.Run(async () =>
            {
                var movies = refreshMainListAsync.Where(x => x.MediaType == GlobalVariableEnumConfiguration.MediaEnum.电影).ToList();
                if (movies.Count > 0)
                {
                    var obtainVideoSubtitleFiles = await VideoListHelper.ObtainVideoSubtitleFiles(movies, getCacheKeyDataQuery.Result, isAll: true);
                    _logger.LogWarning("--电影字幕扫描完成" + obtainVideoSubtitleFiles?.Count);
                    obtainVideoSubtitleFiles?.ForEach(x => data.Add(x));
                }
            }),
            Task.Run(async () =>
            {
                var series = refreshMainListAsync.Where(x => x.MediaType == GlobalVariableEnumConfiguration.MediaEnum.电视剧).ToList();
                if (series.Count > 0)
                {
                    var obtainVideoSubtitleFiles2 = await VideoListHelper.ObtainVideoSubtitleFiles(series, getCacheKeyDataQuery.Result, false, true);
                    _logger.LogWarning("--电视剧字幕扫描完成" + obtainVideoSubtitleFiles2?.Count);
                    obtainVideoSubtitleFiles2?.ForEach(x => data.Add(x));
                }
            })
        };
        await Parallel.ForEachAsync(tasks, async (task, _) => await task);

        // await Task.WhenAll(tasks);
        return data.ToList();
    }

    /// <summary>
    ///     更新媒体数据
    /// </summary>
    /// <param name="getCachePathList"></param>
    /// <returns></returns>
    private async Task<NormalScanVideoResult> ProcessingMediaData(GetCachePathList getCachePathList)
    {
        var requiredService = _serviceScope.ServiceProvider.GetService<ILogger<VideoListHelper>>()!;
        var videoListHelper = new VideoListHelper(requiredService);
        var refreshMainListAsync = await videoListHelper.RefreshMainListAsync(getCachePathList.Result?.ToList(), getCachePathList.TVPlayResult?.ToList(), getCachePathList
            .NoCachePath?.ToList());
        return refreshMainListAsync;
    }

    // 记录文件变化信息的类
    private class FileChangeInfo
    {
        public string Path { get; set; }
        public string ChangeType { get; set; }
        public bool IsMovie { get; set; }
        public bool IsVideoFile { get; set; }
        public bool IsSubtitleFile { get; set; }
        public DateTime ChangeTime { get; set; }
    }
}